package sem.droid;

import java.util.*;

import com.hp.hpl.jena.query.*;
import com.hp.hpl.jena.rdf.model.*;


public class QueryEngine {
	
	protected String m_sEndpoint;
	
	public QueryEngine(String endpoint){
		m_sEndpoint = endpoint;
	}
	
	public List<String> getAll( Iterator<String> iter ){
		List<String> all = new ArrayList<String>();
		while( iter.hasNext() ){
			all.add( (String) iter.next() );
		}
		return all;
	}
	
	protected List<String> execute( String queryString, String sVar, boolean bLiteral ) throws Exception {
		System.out.println( "QueryEngine.query: "+ queryString +"\n" );
		List<String> set = new ArrayList<String>();
		Query query = QueryFactory.create( queryString );
		QueryExecution qe = null;
		try {
			qe = QueryExecutionFactory.sparqlService( m_sEndpoint, query );
			ResultSet results = qe.execSelect();
			while( results.hasNext() )
			{
				QuerySolution soln = results.nextSolution();
				String sValue = null;
				if( bLiteral ){
					Literal l = soln.getLiteral( sVar );
					sValue = l.getString();
				} else {
					Resource r = soln.getResource( sVar );
					sValue = checkURISyntax( r.getURI() );
				}
				if( sValue != null ){
					set.add( sValue );
				}
			}
		}
		catch( Exception e ){
			e.printStackTrace();
		}
		finally {
			if( qe != null ) qe.close();
		}
		return set;
	}
	
	protected TreeMap<Double,String> sortByDistance( Double[] loc, List<String> results ){
		TreeMap<Double,String> hmDist = new TreeMap<Double,String>();
		for( String uri: results ){
			try {
				Double[] targetLocation = getLocation(uri);
				hmDist.put( getDistance(loc, targetLocation), uri );
			}
			catch(Exception e){
				e.printStackTrace();
			}
		}
		//Collections.sort(results, new MyComparator(hmDist));
		return hmDist;
	}
	
	protected Double getDistance( Double[] source, Double[] target ){
		if( target[0] != null && target[1] != null )
		{				
			double latitudeSin  = Math.sin( Math.toRadians(target[0] - source[0]) / 2 );
			double longitudeSin = Math.sin( Math.toRadians(target[1] - source[1]) / 2 );
			double a = latitudeSin * latitudeSin
				+ Math.cos( Math.toRadians(source[0])) * Math.cos(Math.toRadians(target[0]) ) * longitudeSin * longitudeSin;
			double c = 2 * Math.atan2( Math.sqrt(a), Math.sqrt(1 - a) );
			return 6378137 * c;
		}
		return Double.MAX_VALUE;
	}
	
	protected Double[] getLocation( String uri ) throws Exception {
		List<String> lats = execute( "SELECT ?x WHERE { <"+ uri +"> <http://www.w3.org/2003/01/geo/wgs84_pos#lat> ?x . }", "x", true );	
		Double lat = lats.size() > 0 ? Double.parseDouble( lats.get(0) ) : null;
		List<String> lons = execute( "SELECT ?x WHERE { <"+ uri +"> <http://www.w3.org/2003/01/geo/wgs84_pos#long> ?x . }", "x", true );	
		Double lon = lons.size() > 0 ? Double.parseDouble( lons.get(0) ) : null;
		System.out.println( "QueryEngine.getLocation( "+ uri +" ): "+ lat +" / "+ lon );
		return new Double[]{ lat, lon };
	}
	
	protected List<String> getLocatedTypes() {
		try {
			return execute( "SELECT DISTINCT ?x WHERE { ?y <http://www.w3.org/2003/01/geo/wgs84_pos#lat> ?z . ?y <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> ?x . }", "x", true );			
		} catch (Exception e){
			e.printStackTrace();
		}
		return new ArrayList<String>();
	}
	
	protected String getLabel(String uri){
		String label = uri;
		try {
			List<String> labels = execute( "SELECT ?x WHERE { <"+ uri +"> <http://www.w3.org/2000/01/rdf-schema#label> ?x . FILTER( lang(?x) = \"en\" ) . }", "x", true );
			if( labels.size() > 0 ){
				label = labels.get(0);
			}
		} catch( Exception e ){
			e.printStackTrace();
		}
		return label;
	}
		    
    protected String toString(List<String> uris){
    	StringBuffer sb = new StringBuffer();
    	for(String s: uris){
    		String sLabel = getLabel(s);
    		if( sLabel != null ){
    			sb.append(getLabel(s) +"\n");
    		}
    		else {
    			sb.append(s +"\n");
    		}
    	}
    	return sb.toString();
    }
    
    protected String toString(TreeMap<Double,String> map){
    	StringBuffer sb = new StringBuffer();
    	Iterator iter = map.keySet().iterator();
    	while( iter.hasNext() ){
    		Double d = (Double)iter.next();
    		String sd = ( (int)(d/1000) ) +" km";
    		String s = map.get(d);
    		String sLabel = getLabel(s);
    		if( sLabel != null ){
    			sb.append(getLabel(s) +" ("+ sd +")\n");
    		}
    		else {
    			sb.append(s +"("+ sd +")\n");
    		}
    	}
    	return sb.toString();
    }
	
	protected String checkURISyntax( String sURI ){
		if( sURI == null ) return null; 
		String s = new String( sURI );
		s = s.replaceAll( "'", "_" );
		return s;
	}
}

